#pragma once

#include "spdlog/async.h"
#include "spdlog/common.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"
#include <memory>
#include <mutex>
#include <string>

class Logger {
private:
    std::shared_ptr<spdlog::logger> _fileSink;
    std::shared_ptr<spdlog::logger> _console;

    static Logger *_log;
    static std::mutex _mutex;

public:
    bool ends_with(const std::string &str, const std::string &suffix)
    {
        return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
    }

    static Logger *getInstance(void);

    template <typename... Args>
    static void warning(spdlog::format_string_t<Args...> fmt, Args &&...args)
    {
        getInstance()->warn(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
    }

    template <typename... Args>
    static void information(spdlog::format_string_t<Args...> fmt, Args &&...args)
    {
        getInstance()->info(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
    }

    template <typename... Args>
    static void error(spdlog::format_string_t<Args...> fmt, Args &&...args)
    {
        getInstance()->err(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
    }

public:
    Logger() = default;
    Logger(const std::string &sign, const std::string &path);

    void consoleLevel(spdlog::level::level_enum logLevel);
    void fileLevel(spdlog::level::level_enum logLevel);

    void consoleFlushOn(spdlog::level::level_enum logLevel);
    void fileLevelFlushOn(spdlog::level::level_enum logLevel);

    void consoleSetPattern(const std::string &pattern);
    void fileSetPattern(const std::string &pattern);

    template <typename... Args>
    void info(spdlog::format_string_t<Args...> fmt, Args &&...args)
    {
        _console->info(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
        _fileSink->info(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
    }

    template <typename... Args>
    void warn(spdlog::format_string_t<Args...> fmt, Args &&...args)
    {
        _console->warn(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
        _fileSink->warn(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
    }

    template <typename... Args>
    void err(spdlog::format_string_t<Args...> fmt, Args &&...args)
    {
        _console->error(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
        _fileSink->error(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
    }
};

Logger *Logger::_log;
std::mutex Logger::_mutex;

Logger::Logger(const std::string &sign, const std::string &path)
{
    if (ends_with(path, "/")) {
        _fileSink = spdlog::daily_logger_mt<spdlog::async_factory>(sign, path + sign + ".log", 2, 30, false, 2);
    } else {
        _fileSink = spdlog::daily_logger_mt<spdlog::async_factory>(sign, path + "/" + sign + ".log", 2, 30, false, 2);
    }
    _console = spdlog::stdout_color_mt<spdlog::async_factory>("console");

    consoleLevel(spdlog::level::info);
    fileLevel(spdlog::level::info);
    consoleFlushOn(spdlog::level::info);
    fileLevelFlushOn(spdlog::level::info);
    consoleSetPattern("[%Y-%m-%d %T.%e][%n][%^%l%$] : %^%v%$");
    fileSetPattern("[%Y-%m-%d %T.%e][%n][%l] : %v");
}

void Logger::consoleLevel(spdlog::level::level_enum logLevel)
{
    _console->set_level(logLevel);
}

void Logger::fileLevel(spdlog::level::level_enum logLevel)
{
    _fileSink->set_level(logLevel);
}

void Logger::consoleFlushOn(spdlog::level::level_enum logLevel)
{
    _console->flush_on(logLevel);
}

void Logger::fileLevelFlushOn(spdlog::level::level_enum logLevel)
{
    _fileSink->flush_on(logLevel);
}

void Logger::consoleSetPattern(const std::string &pattern)
{
    // 参考 https://github.com/gabime/spdlog/wiki/3.-Custom-formatting
    _console->set_pattern(pattern);
}

void Logger::fileSetPattern(const std::string &pattern)
{
    // 参考 https://github.com/gabime/spdlog/wiki/3.-Custom-formatting
    _fileSink->set_pattern(pattern);
}

Logger *Logger::getInstance(void)
{
    std::unique_lock<std::mutex> _lock(_mutex);
    if (_log) {
        return _log;
    }

    return ((_log = new Logger("dev_modbus", "/userdata")));
}